
import 'dart:io';
import 'package:path/path.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:logger/logger.dart';
import 'package:xim/common/get_style_view.dart';
import 'package:xim/common/util/collection.dart';
import 'package:xim/common/util/ffmpeg.dart';
import 'package:xim/common/util/security.dart';
import 'package:xim/config/store_config.dart';
import 'package:xim/http/apis/gfs/gfs_api.dart';
import 'package:xim/http/apis/message/message_api.dart';
import 'package:xim/http/apis/message/message_model.dart';
import 'package:xim/http/apis/secret/secret_api.dart';
import 'package:xim/http/apis/secret/secret_model.dart';
import 'package:xim/http/apis/session/session_api.dart';
import 'package:xim/http/apis/session/session_model.dart';
import 'package:xim/http/apis/user/user_api.dart';
import 'package:xim/http/apis/user/user_model.dart';
import 'package:xim/screen/message_list_screen/page/chat_detail_page/chat_detail_style.dart';
import 'package:xim/widget/dash_chat_2/dash_chat_2.dart';
import 'package:xim/widget/dash_chat_2/extension/ChatMessageExtension.dart';

class ChatDetailController extends GetxStateMixinController<List<ChatMessage>> {

  /// 会话ID
  final int sessionId;

  /// 日志
  final log = Logger();

  ChatDetailController(this.sessionId);

  /// curr user, 当前用户信息
  late UserInfoModel user;

  /// 正在使用的会话密钥对象
  late SessionSecretKeyModel currSecretKeyModel;

  /// 会话信息
  late SessionModel sessionInfo;

  /// 聊天数据[密文]
  List<ChatMessageModel> chatMessageList = [];

  /// 聊天数据[明文]
  var chatMessages = <ChatMessage>[];

  ChatUser get currChatUser => user.toChatUser();

  /// 会话密钥列表
  List<SessionSecretKeyModel> sessionSecretKeys = [];

  /// 用户私钥（明文）
  late String userPrivateKey;

  /// 正在展示的底部面板
  Rx<BottomPanel?> currBottomPanel = Rx(null);

  /// 关闭底部弹出面板
  void closeBottomPanel(){
    currBottomPanel = Rx(null);
    update();
  }

  /// 正在使用的会话密钥
  String get plainCurrSecretKey => decodeKey(currSecretKeyModel.secretKey);

  /// 打开指定的底部弹出面板
  void openBottomPanel(BottomPanel panel, InputOptions inputOptions, {required BuildContext context}) {
    if (inputOptions.focusNode != null && inputOptions.focusNode!.hasFocus) {
      FocusScope.of(context).requestFocus(FocusNode());
    }
    log.i("open panel ${panel.sortedOrder}");
    currBottomPanel.value = panel;
    update();
  }

  @override
  Future<List<ChatMessage>> dataInit() async {
    // 获取用户信息
    user = await UserApi.queryUserInfo();
    sessionInfo = await SessionApi.querySessionInfoById(sessionId);
    chatMessageList = await MessageApi.queryMessageList(sessionId);
    userPrivateKey = (await StoreApi.instance.secureTakeUserPrivateKey())!;
    // 获取会话密钥
    var currSecretKey = await SecretApi.queryLatestSessionSecretKey(sessionId);
    if(currSecretKey==null){
      // 创建默认的会话密钥
      await createSessionSecretKeys();
      currSecretKey = await SecretApi.queryLatestSessionSecretKey(sessionId);
    }
    currSecretKeyModel = currSecretKey!;
    sessionSecretKeys = await SecretApi.querySessionSecretKeys(sessionId);
    // 会话消息解密
    chatMessages = chatMessageList
        .map((e) => toChatMessage(e))
        .toList();
    chatMessages.sort((e1, e2) => e2.createdAt.compareTo(e1.createdAt));
    return chatMessages;
  }

  /// 发送消息
  void _sendMsg(ChatMessage msg){
    chatMessages.insert(0, msg);
    update();
  }

  /// 发送视频消息
   _sendVideoChatMessage(String originalFileName, String gfsFileName) async {
    var msg = ChatMessageStoreModel(
      userAccount: user.account,
      secretVersion: currSecretKeyModel.secretKeyVersion,
      sessionId: sessionId,
      content: originalFileName,
      chatMessageType: ChatMessageType.video,
      attachment: gfsFileName
    );
    return MessageApi.sendChatMessage(msg);
  }

  Future<String> sendFile(MediaType type, String path, {void Function(int totalCount, int curr)? progress} ) async {
    // 视频处理
    var originalFileName = basename(path);
    var aesKey = SecurityUtility.aes.roundAesKey(size: AesKeySize.small);
    if(type == MediaType.video){
      var m3u8file = await FfmpegUtil.toM3u8(
          M3u8Parameter(
            videoPath: path,
            aesKey:aesKey,
            sliceTime: 10,
            encode:(data)=>encodeText(data, plainCurrSecretKey),
          )
      );
      if(m3u8file!=null){
        var file = m3u8file.m3u8File;
        var tsFiles = await m3u8file.tsFiles;
        if(m3u8file.keyFile!=null){
          await GfsApi.upload(m3u8file.keyFile!, usingOriginalName: true);
        }
        for(var i=0;i<tsFiles.length;i++){
          await GfsApi.upload(tsFiles[i], usingOriginalName: true);
          progress?.call(tsFiles.length, i+1);
        }
        var m3u8fileName = await GfsApi.upload(file);
        var chatMessage = ChatMessage(
          user: currChatUser,
          createdAt: DateTime.now(),
          text: m3u8fileName,
          medias: [
            ChatMedia(url: m3u8fileName, fileName: originalFileName, type: type)
          ],
        );
        await _sendVideoChatMessage(originalFileName, m3u8fileName);
        _sendMsg(chatMessage);
      }
      return '';
    }
    return '';
  }


  /// 修改消息的状态
  void updateMessageStatus(ChatMessage chatMessage, MessageStatus status){
    chatMessage.status= status;
    update();
  }

  /// 发送数据
  void sendMessage(ChatMessage chatMessage) async {
      log.i("send message $chatMessages");
      chatMessages.insert(0, chatMessage);
      updateMessageStatus(chatMessage, MessageStatus.encode);
      // 媒体消息
    if (chatMessage.medias != null && chatMessage.medias!.isNotEmpty) {
      // await _sendMediasMessage(chatMessage.medias![0]);
      // await _sendChunkMediasMessage(chatMessage.medias![0]);
    } else {
      // 文本消息
      var encodeMsg = encodeText(chatMessage.text, plainCurrSecretKey);
      updateMessageStatus(chatMessage, MessageStatus.pending);
      await _sendChatTextMessage(encodeMsg);
    }
    updateMessageStatus(chatMessage, MessageStatus.send);
  }

  /// 更新正在使用的会话密钥
  void updateCurrSecretKey(int secKeyId) async {
    var sessionSecretKey = await SecretApi.querySessionSecretKeyById(secKeyId);
    currSecretKeyModel = sessionSecretKey;
    update();
  }

  /// 分片发送媒体消息
  // Future<String> _sendChunkMediasMessage(ChatMedia chatMedia) async {
  //   var file = File(chatMedia.url);
  //   var fileBytes = file.readAsBytesSync();
  //   var aesKey = await currSecretKeyModel.decodeSecretKey();
  //   var chunkSlice = GfsApi.slice(fileBytes);
  //   log.i("分片完成, 共${chunkSlice.length}个分片, ${DateTime.now().format("yyyy-MM-dd HH:mm:ss")}");
  //   List<Future<List<int>>> encryptChunkSliceFuture = [];
  //   for(var item in chunkSlice){
  //     // var encryptData = AES.encryptAesBytesAsync(data: item, aesKey: aesKey);
  //     var encryptData = AES.encryptAesBytesIsolate(data: item, aesKey: aesKey);
  //     encryptChunkSliceFuture.add(encryptData);
  //   }
  //   var encryptChunkSlice = await Future.wait(encryptChunkSliceFuture);
  //   log.i("分片(size=[${fileBytes.length/1024}])加密完成 ${DateTime.now().format("yyyy-MM-dd HH:mm:ss")}");
  //   return GfsApi.sliceBytesUpload(encryptChunkSlice, file.fileName);
  // }

  // /// 发送媒体消息
  // Future<String> _sendMediasMessage(ChatMedia chatMedia) async {
  //   // aes 加密
  //   var file = File(chatMedia.url);
  //   var fileBytes = file.readAsBytesSync();
  //   var aesKey = await currSecretKeyModel.decodeSecretKey();
  //   log.i("消息(size=[${fileBytes.length/1024}])加密中 ${DateTime.now().format("yyyy-MM-dd HH:mm:ss")}");
  //   var encryptData = encodeText(fileBytes.toList(),  aesKey);
  //   log.i("消息(size=[${fileBytes.length/1024}])加密完成 ${DateTime.now().format("yyyy-MM-dd HH:mm:ss")}");
  //   // upload
  //   var gfsFileName = await GfsApi.uploadBytes(encryptData.toList(), chatMedia.fileName);
  //   await MessageApi.sendChatMessage(ChatMessageStoreModel(
  //     userAccount: user.account,
  //     secretVersion: currSecretKeyModel.secretKeyVersion,
  //     sessionId: sessionInfo.id,
  //     content: gfsFileName,
  //     chatMessageType: ChatMessageType.fromMediaType(chatMedia.type)??ChatMessageType.file,
  //   ));
  //   return GfsApi.getGfsFileDownloadPath(gfsFileName);
  // }


  /// 上传视频
  Future<void> _sendChatVideoMessage(ChatMedia video) async {
    var aesKey = decodeKey(currSecretKeyModel.secretKey);
    var file = File(video.url);
    var fileBytes = await file.readAsBytes();
    var chunkSlice = GfsApi.slice(fileBytes, chunkSize: 10*1024*1024);
    List<Future<List<int>>> encryptChunkSliceFuture = [];
    for(var slice in chunkSlice){
      var tmp = SecurityUtility.aes.encryptAesBytesIsolate(aesKey: aesKey, data: fileBytes);
      encryptChunkSliceFuture.add(tmp);
    }
    var encryptChunkSlice = await Future.wait(encryptChunkSliceFuture);
    await GfsApi.sliceBytesUpload(encryptChunkSlice, video.fileName);

  }

  /// 上传图片
  Future<void> _sendChatImageMessage(ChatMedia image) async {
    var aesKey = decodeKey(currSecretKeyModel.secretKey);
    var file = File(image.url);
    var fileBytes = await file.readAsBytes();
    var encodeData = await SecurityUtility.aes.encryptAesBytesAsync(
        aesKey: aesKey,
        data: fileBytes.toList()
    );
    //  上传加密文件
    var gfsFileName = await GfsApi.uploadBytes(encodeData, image.fileName);
    // 上传图片
    await MessageApi.sendChatMessage(ChatMessageStoreModel(
      userAccount: user.account,
      secretVersion: currSecretKeyModel.secretKeyVersion,
      sessionId: sessionInfo.id,
      content: gfsFileName,
      chatMessageType: ChatMessageType.fromMediaType(image.type) ?? ChatMessageType.file,
    ));
  }

  /// 发送文本数据
  Future<void> _sendChatTextMessage(String text) async {
    var message = ChatMessageStoreModel(
      sessionId: sessionInfo.id,
      content: text,
      userAccount: user.account,
      secretVersion: currSecretKeyModel.secretKeyVersion,
    );
    await MessageApi.sendChatMessage(message);
  }

  /// 解密加密密钥
  String decodeKey(String cipherKey)  {
    return SecurityUtility.sm2.decrypt(cipherKey, userPrivateKey);
  }

  /// 解密加密密钥
  String encodeKey(String plainKey, String hexPubKey){
    return SecurityUtility.sm2.encrypt(plainKey, hexPubKey);
  }

  /// aes 解密
  String decodeText(String cipherText, String aesKey) {
    return SecurityUtility.aes.decryptAes(encryptedBase64Text: cipherText, aseKey: aesKey);
  }

  /// aes 解密
  String encodeText(String plainText, String aesKey) {
    return SecurityUtility.aes.encryptAes(plainText: plainText, aesKey: aesKey);
  }

  /// 类型转换
  ChatMessage toChatMessage(ChatMessageModel messageModel){
    String text  = messageModel.content;
    var plainAesKey = decodeKey(messageModel.secretKey.secretKey);
    if(messageModel.chatMessageType == ChatMessageType.text){
      // 文本消息
      text = decodeText(text, plainAesKey);
    }
    return ChatMessage(
        user: messageModel.userInfo.toChatUser(),
        createdAt: messageModel.sendTime,
        // gfsFileName
        text: text,
        medias: messageModel.chatMessageType == ChatMessageType.text ? null : [
          ChatMedia(
              url: GfsApi.getGfsFileDownloadPath(messageModel.content),
              // gsfFileName
              fileName: messageModel.content,
              // type: chatMessageType.toMediaType()
              type: messageModel.chatMessageType.toMediaType()
          )
        ],
        customProperties: {
          ChatMessageExtensionFields.messageId.key: messageModel.id,
          ChatMessageExtensionFields.secretKey.key: plainAesKey,
          ChatMessageExtensionFields.attachment.key: messageModel.attachment,
        }
    );
  }

  /// 创建会话密钥
  createSessionSecretKeys({String? customSecKey}) async {
    var membersAccount  = await SessionApi.querySessionMembers(sessionId);
    var membersInfo = membersAccount.distinct(func: (e)=>e.account).toList();
    customSecKey ??= SecurityUtility.aes.roundAesKey();
    if(membersInfo.isEmpty){
      return;
    }
    // 加密
    var data = membersInfo
        .map((e) => SecretKeyVo(encodeKey(customSecKey!, e.publicKey!), e.account,))
        .toList();
    await SecretApi.requestCreateSessionSecretKeys(sessionId, data);
    sessionSecretKeys = await SecretApi.querySessionSecretKeys(sessionId);
    update();
  }

}